iT邦幫忙

2023 iThome 鐵人賽

DAY 2
0
Software Development

深入淺出設計模式 - 使用 C++系列 第 2

[Day 02] 讓你的物件掌握現況 - 觀察者模式 (Observer Pattern)

  • 分享至 

  • xImage
  •  

觀察者 (Observer) 模式

  • 定義
    觀察者設計模式是一個非常受歡迎的行為型模式,它允許物件間建立一對多的依賴關係。當一個物件的狀態改變時,所有依賴於它的物件都會收到通知。

  • 關鍵字

    • 幫助物件掌握現況
    • 當感興趣的事發生、立刻知道
    • 一對多關係
    • 鬆耦合

範例 (與書本內的無關)

以下,我們將透過 Solid State Drive (SSD) 和 Driver 的例子,來解釋觀察者模式

1.1 Subject

  • 保持所有觀察者的列表。
  • 提供方法來增加、移除和通知觀察者。
class Observer;

class Subject {
public:
    void addObserver(Observer* obs) {
        observers.push_back(obs);
    }

    void removeObserver(Observer* obs) {
        observers.erase(std::remove(observers.begin(), observers.end(), obs), observers.end());
    }

protected:
    void notify() {
        for (Observer* obs : observers) {
            obs->update();
        }
    }

private:
    vector<Observer*> observers;
};

1.2 Observer

  • 提供更新介面,以便在主題的狀態更改時進行同步。
class Observer {
public:
    virtual void update() = 0;
};

2.1 Concrete Subject

  • 當 SSD 接收到新數據或命令時,通知所有掛鉤的驅動程式和 I/O 裝置
class SSD : public Subject {
public:
    void receiveData(std::string data) {
        this->data = data;
        notify();
    }

    std::string getData() const {
        return data;
    }

private:
    std::string data;
};

2.2 Concrete Observer

  • Kernel I/O Device: 當 SSD 有新數據,它更新其緩存
  • Driver: 會針對 SSD 的新數據進行操作或處理
class KernelIODevice : public Observer {
public:
    KernelIODevice(SSD* drive) : drive(drive) {}

    void update() override {
        std::cout << "Kernel I/O Device updated with data: " << drive->getData() << std::endl;
    }

private:
    SSD* drive;
};

class Driver : public Observer {
public:
    Driver(SSD* drive) : drive(drive) {}

    void update() override {
        std::cout << "Driver processed the data: " << drive->getData() << std::endl;
    }

private:
    SSD* drive;
};

說明

  • 當 SSD 接收到新數據時,KernelIODevice, Driver 會自動獲得通知
int main() {
    SSD mySSD;
    KernelIODevice ioDevice(&mySSD);
    Driver ssdDriver(&mySSD);

    mySSD.addObserver(&ioDevice);
    mySSD.addObserver(&ssdDriver);

    mySSD.receiveData("Operating System Boot Sequence");

    return 0;
}

Output

Kernel I/O Device updated with data: Operating System Boot Sequence
Driver processed the data: Operating System Boot Sequence

小結

  • 透過觀察者模式,物件間的通信變得既簡單又靈活。在我們的 SSD 和 Kernel I/O Device, Driver 的例子中,當 SSD 的狀態變更,相關的元件可以迅速地獲得通知和反應
  • 這種模式適用於任何需要跨多個物件同步的情境

發布者 + 訂閱者 = 觀察者模式

註: GoF 書中提到的 Pub-Sub 與我們現代軟體開發使用的 Pub-Sub 還是有些不同,不可完全視作等號!

發布者 (Publisher):

  • 也稱為 Subject。它是資訊的源頭,當其內部狀態改變時,它會通知所有的訂閱者
    • 負責管理和追踪所有的訂閱者。
    • 提供接口,使得訂閱者可以訂閱或取消訂閱。

訂閱者 (Subscriber):

  • 也稱為觀察者 (Observer)。它對一個或多個發布者有興趣,並希望被通知當這些發布者的狀態變化時
  • 通常提供一個回調或更新函數,該函數將在相應的發布者變更時被調用
  • 在真實生活中,考慮一家雜誌出版社:當有新的雜誌版本發布時,所有的訂閱者都會收到新的雜誌。在這裡,出版社是發布者,而那些訂閱雜誌的人是訂閱者
  • 將此轉移到軟體領域:當一個物件(發布者/主題)的狀態變更時,它會通知所有註冊的物件(訂閱者/觀察者)
  • 這種模式允許系統的這些部分保持解耦,使得發布者可以獨立於其訂閱者而操作,且無需知道訂閱者的具體實現。這也意味著在系統運行時,可以動態地添加或移除訂閱者,使得系統具有高度的靈活性和可擴展性。

回到我們的 SSD 和 Kernel I/O Device, Driver 的例子,SSD 可以被視為發布者,而 Kernel I/O Device 和 Driver 是訂閱者。當 SSD 接收到新數據或命令時(即狀態改變),它會通知所有的訂閱者,這些訂閱者隨後可以執行各自的操作,如更新緩存或處理數據

Reference

[1]. https://notfalse.net/11/pub-sub-pattern
[2]. https://www.agilecaterpillar.com/blog/observer/


上一篇
[Day 01] 什麼是設計模式 (Design Patterns) ? 開始學習Pattern前該知道的觀念
下一篇
[Day 03] 把會變的部分封裝 - 策略模式 (Strategy Pattern)
系列文
深入淺出設計模式 - 使用 C++37
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言